package cn.iocoder.yudao.module.system.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;

/**
 * FFmpeg工具类
 * 通过调用系统终端执行FFmpeg命令进行图片压缩和音频转换
 *
 * @author 芋道源码
 */
@Slf4j
public class FFmpegUtil {

    // FFmpeg命令路径，如果系统PATH中有FFmpeg，可以直接使用"ffmpeg"
    private static final String FFMPEG_COMMAND = "ffmpeg";
    
    // 图片压缩配置
    private static final int MAX_IMAGE_WIDTH = 1280;  // 降低最大宽度
    private static final int MAX_IMAGE_HEIGHT = 720;  // 降低最大高度
    private static final int IMAGE_QUALITY = 30 ;      // 降低JPEG质量到30%
    
    // 高级压缩配置
    private static final boolean ENABLE_AGGRESSIVE_COMPRESSION = true;  // 启用激进压缩
    private static final int PNG_COMPRESSION_LEVEL = 9;                 // PNG最高压缩级别
    private static final int WEBP_COMPRESSION_LEVEL = 6;                // WebP压缩级别
    private static final int GIF_FRAME_RATE = 8;                        // GIF帧率降低到8fps
    
    // 音频转换配置
    private static final int AUDIO_BITRATE = 64;  // 降低比特率到64kbps（原128kbps）
    private static final int AUDIO_SAMPLE_RATE = 22050; // 降低采样率到22.05kHz（原44.1kHz）
    private static final int AUDIO_CHANNELS = 1;  // 单声道（原立体声）
    private static final int AUDIO_COMPRESSION_LEVEL = 8; // AAC压缩级别（0-10，越高压缩越大）
    
    // 命令执行超时时间
    private static final int COMMAND_TIMEOUT_SECONDS = 300; // 5分钟

    /**
     * 压缩图片文件
     * 
     * @param sourcePath 源图片路径
     * @param targetPath 目标图片路径
     * @return 是否成功
     */
    public static boolean compressImage(String sourcePath, String targetPath) {
        try {
            // 记录开始信息
            log.info("=== 开始图片压缩 ===");
            log.info("源文件: {}", sourcePath);
            log.info("目标文件: {}", targetPath);
            
            // 检查源文件是否存在
            File sourceFile = new File(sourcePath);
            if (!sourceFile.exists()) {
                String errorMsg = "源图片文件不存在: " + sourcePath;
                log.error(errorMsg);
                System.out.println("错误: " + errorMsg);
                return false;
            }
            
            // 创建目标目录
            File targetFile = new File(targetPath);
            targetFile.getParentFile().mkdirs();
            
            // 获取文件扩展名
            String extension = getFileExtension(sourcePath).toLowerCase();
            log.debug("文件扩展名: {}", extension);
            
            // 构建FFmpeg命令
            String command;
            if (".jpg".equals(extension) || ".jpeg".equals(extension)) {
                // JPEG格式：使用质量参数 + 额外压缩优化
                command = String.format(
                    "%s -i \"%s\" -vf \"scale=min(%d\\,iw):min(%d\\,ih):force_original_aspect_ratio=decrease\" " +
                    "-q:v %d -preset slow -tune stillimage -y \"%s\"",
                    FFMPEG_COMMAND, sourcePath, MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT, 
                    IMAGE_QUALITY, targetPath
                );
            } else if (".png".equals(extension)) {
                // PNG格式：使用最高压缩级别 + 调色板优化
                command = String.format(
                    "%s -i \"%s\" -vf \"scale=min(%d\\,iw):min(%d\\,ih):force_original_aspect_ratio=decrease\" " +
                    "-compression_level %d -pix_fmt pal8 -y \"%s\"",
                    FFMPEG_COMMAND, sourcePath, MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT, 
                    PNG_COMPRESSION_LEVEL, targetPath
                );
            } else if (".gif".equals(extension)) {
                // GIF格式：优化调色板和帧率
                command = String.format(
                    "%s -i \"%s\" -vf \"scale=min(%d\\,iw):min(%d\\,ih):force_original_aspect_ratio=decrease\" " +
                    "-pix_fmt pal8 -r %d -y \"%s\"",
                    FFMPEG_COMMAND, sourcePath, MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT, 
                    GIF_FRAME_RATE, targetPath
                );
            } else if (".webp".equals(extension)) {
                // WebP格式：使用最高压缩
                command = String.format(
                    "%s -i \"%s\" -vf \"scale=min(%d\\,iw):min(%d\\,ih):force_original_aspect_ratio=decrease\" " +
                    "-quality %d -compression_level %d -y \"%s\"",
                    FFMPEG_COMMAND, sourcePath, MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT, 
                    IMAGE_QUALITY, WEBP_COMPRESSION_LEVEL, targetPath
                );
            } else {
                // 其他格式：调整尺寸 + 基本压缩
                command = String.format(
                    "%s -i \"%s\" -vf \"scale=min(%d\\,iw):min(%d\\,ih):force_original_aspect_ratio=decrease\" " +
                    "-q:v %d -y \"%s\"",
                    FFMPEG_COMMAND, sourcePath, MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT, 
                    IMAGE_QUALITY, targetPath
                );
            }
            
            log.info("执行图片压缩命令: {}", command);
            
            // 执行命令
            boolean success = executeCommand(command);
            
            if (success) {
                // 检查目标文件是否生成
                if (new File(targetPath).exists()) {
                    long originalSize = sourceFile.length();
                    long compressedSize = new File(targetPath).length();
                    double compressionRatio = (double) compressedSize / originalSize;
                    
                    String successMsg = String.format("图片压缩成功: %s -> %s, 压缩比: %.2f%%", 
                            sourcePath, targetPath, compressionRatio * 100);
                    log.info(successMsg);
                    return true;
                } else {
                    String errorMsg = "图片压缩失败：目标文件未生成: " + targetPath;
                    log.error(errorMsg);
                    return false;
                }
            } else {
                String errorMsg = "图片压缩命令执行失败: " + sourcePath;
                log.error(errorMsg);
                return false;
            }
            
        } catch (Exception e) {
            String errorMsg = "图片压缩过程中发生异常: " + sourcePath + " -> " + targetPath + ", " + e.getMessage();
            log.error(errorMsg, e);
            return false;
        }
    }

    /**
     * 转换音频为AAC格式
     * 
     * @param sourcePath 源音频路径
     * @param targetPath 目标AAC路径
     * @return 是否成功
     */
    public static boolean convertAudioToAAC(String sourcePath, String targetPath) {
        try {
            // 记录开始信息
            log.info("=== 开始音频转换 ===");
            log.info("源文件: {}", sourcePath);
            log.info("目标文件: {}", targetPath);
            
            // 检查源文件是否存在
            File sourceFile = new File(sourcePath);
            if (!sourceFile.exists()) {
                String errorMsg = "源音频文件不存在: " + sourcePath;
                log.error(errorMsg);
                System.out.println("错误: " + errorMsg);
                return false;
            }
            
            // 创建目标目录
            File targetFile = new File(targetPath);
            targetFile.getParentFile().mkdirs();
            
            // 构建FFmpeg命令：转换为AAC格式，使用激进压缩参数
            String command = String.format(
                "%s -i \"%s\" -c:a aac -b:a %dk -ar %d -ac %d -compression_level %d -y \"%s\"",
                FFMPEG_COMMAND, sourcePath, AUDIO_BITRATE, AUDIO_SAMPLE_RATE, 
                AUDIO_CHANNELS, AUDIO_COMPRESSION_LEVEL, targetPath
            );
            
            log.info("执行音频转换命令: {}", command);
            
            // 执行命令
            boolean success = executeCommand(command);
            
            if (success) {
                // 检查目标文件是否生成
                if (new File(targetPath).exists()) {
                    long originalSize = sourceFile.length();
                    long convertedSize = new File(targetPath).length();
                    double compressionRatio = (double) convertedSize / originalSize;
                    
                    String successMsg = String.format("音频转换成功: %s -> %s, 压缩比: %.2f%%", 
                            sourcePath, targetPath, compressionRatio * 100);
                    log.info(successMsg);
                    return true;
                } else {
                    String errorMsg = "音频转换失败：目标文件未生成: " + targetPath;
                    log.error(errorMsg);
                    return false;
                }
            } else {
                String errorMsg = "音频转换命令执行失败: " + sourcePath;
                log.error(errorMsg);
                return false;
            }
            
        } catch (Exception e) {
            String errorMsg = "音频转换过程中发生异常: " + sourcePath + " -> " + targetPath + ", " + e.getMessage();
            log.error(errorMsg, e);
            return false;
        }
    }

    /**
     * 执行FFmpeg命令
     * 
     * @param command 要执行的命令
     * @return 是否成功
     */
    private static boolean executeCommand(String command) {
        Process process = null;
        try {
            log.debug("开始执行FFmpeg命令...");
            
            // 根据操作系统选择命令执行方式
            String[] cmdArray;
            if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                cmdArray = new String[]{"cmd", "/c", command};
                log.debug("Windows系统，使用cmd执行");
            } else {
                cmdArray = new String[]{"/bin/sh", "-c", command};
                log.debug("Unix系统，使用sh执行");
            }
            
            // 创建进程
            ProcessBuilder processBuilder = new ProcessBuilder(cmdArray);
            processBuilder.redirectErrorStream(true);
            process = processBuilder.start();
            
            log.debug("进程已启动，等待执行完成...");
            
            // 读取输出
            StringBuilder output = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream(), "UTF-8"))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                    log.debug("FFmpeg输出: {}", line);
                }
            }
            
            // 等待进程完成
            boolean finished = process.waitFor(COMMAND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
            
            if (!finished) {
                String errorMsg = "FFmpeg命令执行超时: " + command;
                log.error(errorMsg);
                process.destroyForcibly();
                return false;
            }
            
            int exitCode = process.exitValue();
            log.debug("FFmpeg命令执行完成，退出码: {}", exitCode);
            
            if (exitCode == 0) {
                log.debug("FFmpeg命令执行成功，输出: {}", output.toString());
                return true;
            } else {
                String errorMsg = String.format("FFmpeg命令执行失败，退出码: %d, 输出: %s", exitCode, output.toString());
                log.error(errorMsg);
                return false;
            }
            
        } catch (Exception e) {
            String errorMsg = "执行FFmpeg命令时发生异常: " + command + ", " + e.getMessage();
            log.error(errorMsg, e);
            return false;
        } finally {
            if (process != null) {
                process.destroyForcibly();
                log.debug("进程已清理");
            }
        }
    }

    /**
     * 检查FFmpeg是否可用
     * 
     * @return 是否可用
     */
    public static boolean isFFmpegAvailable() {
        try {
            log.debug("检查FFmpeg是否可用...");
            String command = FFMPEG_COMMAND + " -version";
            log.debug("执行命令: {}", command);
            
            String[] cmdArray;
            if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                cmdArray = new String[]{"cmd", "/c", command};
                log.debug("Windows系统，使用cmd执行");
            } else {
                cmdArray = new String[]{"/bin/sh", "-c", command};
                log.debug("Unix系统，使用sh执行");
            }
            
            ProcessBuilder processBuilder = new ProcessBuilder(cmdArray);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();
            
            log.debug("等待FFmpeg版本检查完成...");
            boolean finished = process.waitFor(10, TimeUnit.SECONDS);
            if (!finished) {
                log.debug("FFmpeg版本检查超时");
                process.destroyForcibly();
                return false;
            }
            
            int exitCode = process.exitValue();
            boolean available = exitCode == 0;
            log.debug("FFmpeg可用性检查完成，退出码: {}, 可用: {}", exitCode, available);
            
            return available;
            
        } catch (Exception e) {
            String errorMsg = "检查FFmpeg可用性时发生异常: " + e.getMessage();
            log.error(errorMsg, e);
            return false;
        }
    }

    /**
     * 获取文件扩展名
     * 
     * @param fileName 文件名
     * @return 扩展名（包含点）
     */
    private static String getFileExtension(String fileName) {
        if (!StringUtils.hasText(fileName)) {
            return "";
        }
        
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
            return fileName.substring(lastDotIndex);
        }
        return "";
    }

        /**
     * 获取文件大小（MB）
     *
     * @param filePath 文件路径
     * @return 文件大小（MB）
     */
    public static double getFileSizeMB(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            return (double) file.length() / (1024 * 1024);
        }
        return 0.0;
    }

    /**
     * 获取压缩统计信息
     *
     * @param sourcePath 源文件路径
     * @param targetPath 目标文件路径
     * @return 压缩统计信息字符串
     */
    public static String getCompressionStats(String sourcePath, String targetPath) {
        try {
            File sourceFile = new File(sourcePath);
            File targetFile = new File(targetPath);
            
            if (!sourceFile.exists() || !targetFile.exists()) {
                return "文件不存在，无法计算压缩统计";
            }
            
            long originalSize = sourceFile.length();
            long compressedSize = targetFile.length();
            double compressionRatio = (double) compressedSize / originalSize;
            double sizeReduction = (1.0 - compressionRatio) * 100;
            
            return String.format(
                "压缩统计: 原始大小: %.2f MB, 压缩后: %.2f MB, 压缩比: %.1f%%, 减少: %.1f%%",
                (double) originalSize / (1024 * 1024),
                (double) compressedSize / (1024 * 1024),
                compressionRatio * 100,
                sizeReduction
            );
            
        } catch (Exception e) {
            return "计算压缩统计时发生错误: " + e.getMessage();
        }
    }

    /**
     * 获取音频压缩配置信息
     *
     * @return 音频压缩配置信息字符串
     */
    public static String getAudioCompressionConfig() {
        return String.format(
            "音频压缩配置: 比特率=%dkbps, 采样率=%dHz, 声道数=%d, 压缩级别=%d",
            AUDIO_BITRATE, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_COMPRESSION_LEVEL
        );
    }
} 